/*
 * GraphBoard.java
 */

package complexchaos;

import java.beans.*;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;

import java.io.Serializable;
import javax.swing.JPanel;

/**
 * A class to allow for easily drawing points inside of a JPanel. Thanks to Sam
 * Powell for help with this.
 *
 * @author Dan Kotowski
 * @see JPanel
 */
public class GraphBoard extends JPanel implements Serializable {
	private static final long serialVersionUID = 1L;
	private BufferedImage image;
	private Graphics2D graphics;
	private Color bg;
    private PropertyChangeSupport propertySupport;

    /**
     * Class default constructor. Makes the graph 400x400 with a black background.
     */
    public GraphBoard() {
        this(400, 400, Color.BLACK);
    }

    /**
     * Class constructor.
     * @param w graph width
     * @param h graph height
     * @param background graph background color
     */
    public GraphBoard(int w, int h, Color background) {
		super();
		image = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
		graphics = image.createGraphics();
		graphics.setBackground(background);
		graphics.clearRect(0, 0, w, h);
		Dimension size = new Dimension(w, h);
		setPreferredSize(size);
		setMaximumSize(size);
		setMinimumSize(size);
		bg = background;
        propertySupport = new PropertyChangeSupport(this);
	}

    /**
     * Changes the graph background color.
     * @param background new background color
     */
    public void setImageBackground(Color background) {
        bg = background;
        graphics.setBackground(bg);
        graphics.clearRect(0, 0, image.getWidth(), image.getHeight());
    }

    /**
     * Changes the graph size.
     * @param d new graph dimensions
     * @see Dimension
     */
    public void setImageSize(Dimension d) {
        image = new BufferedImage(d.width, d.height, BufferedImage.TYPE_INT_ARGB);
        setPreferredSize(d);
        setMaximumSize(d);
        setMinimumSize(d);
    }

    /**
     * Displays a Graphics object on the graph.
     * @param g Graphics object to display
     * @see Graphics
     */
    @Override
    public void paintComponent(Graphics g) {
		super.paintComponent(g);
		g.drawImage(image, 0, 0, bg, null);
	}

    /**
     * Returns the RGB value of the point (x,y) on the graph.
     * @param x x value
     * @param y y value
     * @return RGB value of point (x,y)
     */
	public int getRGB(int x, int y) {
		return image.getRGB(x, y);
	}

    /**
     * Returns the Color of the point (x,y) on the graph.
     * @param x x value
     * @param y y value
     * @return Color of the point (x,y)
     */
	public Color getRGBColor(int x, int y) {
		return new Color(image.getRGB(x, y));
	}

    /**
     * Sets the RGB value of a point (x,y) on the graph.
     * @param x x value
     * @param y y value
     * @param rgb RGB value to assign
     */
	public void setRGB(int x, int y, int rgb) {
		image.setRGB(x, y, rgb);
	}

    /**
     * Sets the Color of a point (x,y) on the graph.
     * @param x x value
     * @param y y value
     * @param c Color to assign
     */
	public void setRGBColor(int x, int y, Color c) {
		image.setRGB(x, y, c.getRGB());
	}

    /**
     * Mixes a new color with the current one at point (x,y).
     * @param x x value
     * @param y y value
     * @param c color to mix
     * @param alpha level of mixture in terms of alpha
     */
	public void mixRGB(int x, int y, Color c, double alpha) {
		Color c2 = getRGBColor(x, y);
		double ahpla = 1-alpha;
		setRGBColor(x, y, new Color((int)(alpha*c.getRed()+ahpla*c2.getRed()),
				(int)(alpha*c.getGreen()+ahpla*c2.getGreen()),
				(int)(alpha*c.getBlue()+ahpla*c2.getBlue()),
				(int)(alpha*c.getAlpha()+ahpla*c2.getAlpha())));
	}

    /**
     * Draws a point at (x,y) with color c.
     * @param x x value
     * @param y y value
     * @param c color as an int
     */
	public void drawPoint(double x, double y, int c) {
		drawPoint(x,y,new Color(c));
	}

    /**
     * Draws a point at (x,y) with color c.
     * @param x x value
     * @param y y value
     * @param c color
     * @see Color
     */
	public void drawPoint(double x, double y, Color c) {
		double alpha = c.getAlpha()/255.0;
		int xx = (int)x;
		int yy = (int)y;
		double dx2 = x - xx; //same as x - floor(x)
		double dy2 = y - yy;
		double dx1 = 1 - dx2;
		double dy1 = 1 - dy2;
		double a00 = alpha*dx1*dy1;
		double a10 = alpha*dx2*dy1;
		double a01 = alpha*dx1*dy2;
		double a11 = alpha*dx2*dy2;
		mixRGB(xx,yy,c,a00);
		if(xx+1 < image.getWidth())
			mixRGB(xx+1,yy,c,a10);
		if(yy+1 < image.getHeight()) {
			mixRGB(xx,yy+1,c,a01);
			if(xx+1 < image.getWidth())
				mixRGB(xx+1,yy+1,c,a11);
		}
	}

    /**
     * Returns the graph so that it can be used.
     * @return the graph as a Graphics2D object
     * @see Graphics2D
     */
    public Graphics2D getGraphics2D() {
		return graphics;
	}
}
